/*
 * The MIT License
 *
 * Copyright (c) 2010, Bharathwaaj Srinivasan, bluegenetic@gmail.com, bharathwaaj.s@gmail.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "curltweet_api.h"
#include "private.h"

size_t ct_header_callback(void *p_v_data, size_t size, size_t nmemb, void *p_v_userdata)
{
    uint8_t *p_u8_data = (uint8_t *)(p_v_data);
    int32_t s32_bytesReceived = (size * nmemb);
    CurlTweet p_s_ctHandle = (CurlTweet )(p_v_userdata);

    if ((*p_u8_data == '\r') && (*(p_u8_data + 1) == '\n'))
        printf("Header fully received\n");
    else {
        if (0 == strncmp(p_u8_data, "Content-Length", strlen("Content-Length")))
        {
            p_u8_data += strlen("Content-Length");
            while ((*p_u8_data) != ':')
            {
                p_u8_data++;
            }
            p_u8_data++;
            while ((*p_u8_data) == ' ')
            {
                p_u8_data++;
            }
            // Get Content Length here
            p_s_ctHandle->u32_contentLength = atoi(p_u8_data);
            p_s_ctHandle->p_u8_payload = (uint8_t *)malloc(sizeof(uint8_t) * (p_s_ctHandle->u32_contentLength));
            if (p_s_ctHandle->p_u8_payload == NULL)
            {
                printf("Error memory allocation\n");
            }
            else
            {
                printf("Memory allocated for payload = %d\n", p_s_ctHandle->u32_contentLength);
            }
        }
    }

    return s32_bytesReceived;
}

size_t ct_message_callback(void *p_v_data, size_t size, size_t nmemb, void *p_v_userdata)
{
    CurlTweet p_s_ctHandle  = (CurlTweet )(p_v_userdata);
    int32_t s32_bytesReceived = (size * nmemb);
    memcpy((p_s_ctHandle->p_u8_payload + p_s_ctHandle->u32_dataReceived), p_v_data, s32_bytesReceived);
    p_s_ctHandle->u32_dataReceived += s32_bytesReceived;
    if (p_s_ctHandle->u32_dataReceived == p_s_ctHandle->u32_contentLength) {
        /* Indicate the main api who is waiting that data is fully received */
        printf("%s\n", p_s_ctHandle->p_u8_payload);
        p_s_ctHandle->s32_dataFullyReceived = 1;
    }

    return s32_bytesReceived;
}

CtError ct_prepare_nw(CurlTweet p_s_ctHandle, uint8_t *p_u8_url, uint8_t *p_u8_postData)
{
    CURLcode e_curlretval = CURLE_OK;
    CtError e_retval = ERR_NONE;

    curl_easy_reset(p_s_ctHandle->p_s_curlHandle);

    e_curlretval = curl_easy_setopt(p_s_ctHandle->p_s_curlHandle, CURLOPT_FOLLOWLOCATION, 1);
    if (e_curlretval != CURLE_OK) {
        e_retval = ERR_INTERNAL;
        goto END_ct_prepare_nw;
    }

    e_curlretval = curl_easy_setopt(p_s_ctHandle->p_s_curlHandle, CURLOPT_HEADERFUNCTION, ct_header_callback);
    if (e_curlretval != CURLE_OK) {
        e_retval = ERR_INTERNAL;
        goto END_ct_prepare_nw;
    }

    e_curlretval = curl_easy_setopt(p_s_ctHandle->p_s_curlHandle, CURLOPT_WRITEHEADER, p_s_ctHandle);
    if (e_curlretval != CURLE_OK) {
        e_retval = ERR_INTERNAL;
        goto END_ct_prepare_nw;
    }

    e_curlretval = curl_easy_setopt(p_s_ctHandle->p_s_curlHandle, CURLOPT_WRITEFUNCTION, ct_message_callback);
    if (e_curlretval != CURLE_OK) {
        e_retval = ERR_INTERNAL;
        goto END_ct_prepare_nw;
    }

    e_curlretval = curl_easy_setopt(p_s_ctHandle->p_s_curlHandle, CURLOPT_WRITEDATA, p_s_ctHandle);
    if (e_curlretval != CURLE_OK) {
        e_retval = ERR_INTERNAL;
        goto END_ct_prepare_nw;
    }

    e_curlretval = curl_easy_setopt(p_s_ctHandle->p_s_curlHandle, CURLOPT_USERNAME, p_s_ctHandle->a_u8_username);
    if (e_curlretval != CURLE_OK) {
        e_retval = ERR_INTERNAL;
        goto END_ct_prepare_nw;
    }

    e_curlretval = curl_easy_setopt(p_s_ctHandle->p_s_curlHandle, CURLOPT_PASSWORD, p_s_ctHandle->a_u8_passwd);
    if (e_curlretval != CURLE_OK) {
        e_retval = ERR_INTERNAL;
        goto END_ct_prepare_nw;
    }

    e_curlretval = curl_easy_setopt(p_s_ctHandle->p_s_curlHandle, CURLOPT_URL, p_u8_url);
    if (e_curlretval != CURLE_OK) {
        e_retval = ERR_INTERNAL;
        goto END_ct_prepare_nw;
    }

    if (p_u8_postData != NULL) {
        e_curlretval = curl_easy_setopt(p_s_ctHandle->p_s_curlHandle, CURLOPT_POSTFIELDS, p_u8_postData);
        if (e_curlretval != CURLE_OK) {
            e_retval = ERR_INTERNAL;
            goto END_ct_prepare_nw;
        }
    }

    e_curlretval = curl_easy_perform(p_s_ctHandle->p_s_curlHandle);
    if (e_curlretval != CURLE_OK) {
        e_retval = ERR_INTERNAL;
        goto END_ct_prepare_nw;
    }

END_ct_prepare_nw:
    return e_retval;
}

CtError ct_parse_userInfo(ezxml_t p_s_statusToken, UserInfo *p_s_userInfo)
{
    CtError e_retval = ERR_NONE;
    ezxml_t p_s_token = NULL;

    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "id", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->u64_userId = atoll(p_s_token->txt);
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "name", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->p_u8_fullname = p_s_token->txt;
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "screen_name", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->p_u8_username = p_s_token->txt;
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "location", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->p_u8_location = p_s_token->txt;
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "description", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->p_u8_description = p_s_token->txt;
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "profile_image_url", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->p_u8_profImgUrl = p_s_token->txt;
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "url", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->p_u8_userUrl = p_s_token->txt;
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "protected", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->eb_protected = strcmp(p_s_token->txt, "false") ? CT_TRUE : CT_FALSE;
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "followers_count", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->u32_numFollowers = atoi(p_s_token->txt);
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "profile_background_color", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->p_u8_profBgColor = p_s_token->txt;
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "profile_text_color", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->p_u8_profTextColor = p_s_token->txt;
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "profile_link_color", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->p_u8_profLinkColor = p_s_token->txt;
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "profile_sidebar_fill_color", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->p_u8_sideBarFillColor = p_s_token->txt;
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "profile_sidebar_border_color", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->p_u8_sideBarBorderColor = p_s_token->txt;
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "friends_count", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->u32_numFriends = atoi(p_s_token->txt);
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "created_at", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->p_u8_profCreationTime = p_s_token->txt;
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "favourites_count", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->u32_numFavourites = atoi(p_s_token->txt);
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "utc_offset", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->s32_utcOffset = atoi(p_s_token->txt);
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "time_zone", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->p_u8_timeZone = p_s_token->txt;
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "profile_background_image_url", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->p_u8_profImgUrl = p_s_token->txt;
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "profile_background_tile", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->eb_bgTile = strcmp(p_s_token->txt, "false") ? CT_TRUE : CT_FALSE;
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "notifications", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->eb_notifications = strcmp(p_s_token->txt, "false") ? CT_TRUE : CT_FALSE;
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "geo_enabled", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->eb_geoEnabled = strcmp(p_s_token->txt, "false") ? CT_TRUE : CT_FALSE;
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "verified", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->eb_verified = strcmp(p_s_token->txt, "false") ? CT_TRUE : CT_FALSE;
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "statuses_count", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->u32_numOfStatuses = atoi(p_s_token->txt);
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "lang", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->p_u8_lang = p_s_token->txt;
    p_s_token = ezxml_get(p_s_statusToken,  "user", 0, "contributors_enabled", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_userInfo->eb_contribEnabled = strcmp(p_s_token->txt, "false") ? CT_TRUE : CT_FALSE;

    /* TODO Parse Geo Enabled structure */
    /* TODO Parse Contributors Enabled structure */
    return e_retval;
}
#if 0
{
    CtError e_retval = ERR_NONE;
    ezxml_t p_s_token = NULL;

    p_s_token = ezxml_get(p_s_statusToken,  "status", s32_index, "created_at", 0, NULL, -1);
    if (p_s_token != NULL)
    {
        p_s_status->p_u8_postTime = p_s_token->txt;
    }
    p_s_token = ezxml_get(p_s_statusToken,  "status", s32_index, "id", 0, NULL, -1);
    if (p_s_token != NULL)
    {
        p_s_status->u64_statusId = atoll(p_s_token->txt);
    }
    p_s_token = ezxml_get(p_s_statusToken,  "status", s32_index, "text", 0, NULL, -1);
    if (p_s_token != NULL)
    {
        p_s_status->p_u8_tweet = p_s_token->txt;
    }

    p_s_token = ezxml_get(p_s_statusToken,  "status", s32_index, "source", 0, NULL, -1);
    if (p_s_token != NULL)
    {
        /* TODO */
    }

    p_s_token = ezxml_get(p_s_statusToken,  "status", s32_index, "truncated", 0, NULL, -1);
    if (p_s_token != NULL)
    {
        p_s_status->eb_truncated = strcmp(p_s_token->txt, "false") ? CT_TRUE : CT_FALSE;
    }

    p_s_token = ezxml_get(p_s_statusToken,  "status", s32_index, "in_reply_to_status_id", 0, NULL, -1);
    if (p_s_token != NULL)
    {
        p_s_status->u64_replyToStatusId = atoll(p_s_token->txt);
    }

    p_s_token = ezxml_get(p_s_statusToken,  "status", s32_index, "in_reply_to_user_id", 0, NULL, -1);
    if (p_s_token != NULL)
    {
        p_s_status->u64_replyToUserId = atoll(p_s_token->txt);
    }

    p_s_token = ezxml_get(p_s_statusToken,  "status", s32_index, "favorited", 0, NULL, -1);
    if (p_s_token != NULL)
    {
        p_s_status->eb_favorited = strcmp(p_s_token->txt, "false") ? CT_TRUE : CT_FALSE;
    }

    p_s_token = ezxml_get(p_s_statusToken,  "status", s32_index, "in_reply_to_screen_name", 0, NULL, -1);
    if (p_s_token != NULL)
    {
        p_s_status->p_u8_replyToUsername = p_s_token->txt;
    }

    p_s_token = ezxml_get(p_s_statusToken,  "status", s32_index, NULL, -1);
    if (p_s_token != NULL)
    {
        e_retval = ct_parse_userInfo(p_s_token, &(p_s_status->s_userInfo));
    }

    return e_retval;
}
#endif

CtError ct_parse_status(ezxml_t p_s_statusToken, Status *p_s_status, uint32_t s32_index)
{
    CtError e_retval = ERR_NONE;
    ezxml_t p_s_token = NULL;

    p_s_token = ezxml_get(p_s_statusToken,  "created_at", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_status->p_u8_postTime = p_s_token->txt;
    p_s_token = ezxml_get(p_s_statusToken,  "id", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_status->u64_statusId = atoll(p_s_token->txt);
    p_s_token = ezxml_get(p_s_statusToken,  "text", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_status->p_u8_tweet = p_s_token->txt;

    p_s_token = ezxml_get(p_s_statusToken,  "source", 0, NULL, -1);
    if (p_s_token != NULL)
    {
        /* TODO */
    }

    p_s_token = ezxml_get(p_s_statusToken,  "truncated", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_status->eb_truncated = strcmp(p_s_token->txt, "false") ? CT_TRUE : CT_FALSE;

    p_s_token = ezxml_get(p_s_statusToken,  "in_reply_to_status_id", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_status->u64_replyToStatusId = atoll(p_s_token->txt);

    p_s_token = ezxml_get(p_s_statusToken,  "in_reply_to_user_id", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_status->u64_replyToUserId = atoll(p_s_token->txt);

    p_s_token = ezxml_get(p_s_statusToken,  "favorited", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_status->eb_favorited = strcmp(p_s_token->txt, "false") ? CT_TRUE : CT_FALSE;

    p_s_token = ezxml_get(p_s_statusToken,  "in_reply_to_screen_name", 0, NULL, -1);
    if (p_s_token != NULL)
        p_s_status->p_u8_replyToUsername = p_s_token->txt;

    e_retval = ct_parse_userInfo(p_s_statusToken, &(p_s_status->s_userInfo));

    return e_retval;
}

CtError ct_parse_statuses(ezxml_t p_s_xml, StatusRoot *p_s_statuses)
{
    Status *p_s_statusesArr = NULL;
    int32_t s32_count = 0, s32_max = 0;
    ezxml_t p_s_token;
    CtError e_retval = ERR_NONE;

    while (1) {
        p_s_token = ezxml_get(p_s_xml,  "status", s32_max, NULL, -1);
        if (p_s_token == NULL) {
            printf("Number of statuses found %d\n", s32_max);
            break;
        }
        s32_max++;
    }

    p_s_statuses->p_s_statusesArr = (Status *)malloc(sizeof(Status ) * s32_max);
    if (p_s_statuses->p_s_statusesArr == NULL) {
        e_retval = ERR_INTERNAL;
        goto END_ct_parse_statuses;
    }
    p_s_statuses->u32_numOfStatuses = s32_max;
    p_s_statusesArr = p_s_statuses->p_s_statusesArr;

    s32_count = 0;
    while (s32_count != s32_max) {
        p_s_token = ezxml_get(p_s_xml,  "status", s32_count, NULL, -1);
        if (p_s_token == NULL) {
            printf("Number of statuses found %d\n", s32_max);
            break;
        }
        e_retval = ct_parse_status(p_s_token, p_s_statusesArr, s32_count);
        if (e_retval != ERR_NONE) {
            goto END_ct_parse_statuses;
        }
        s32_count++;
        p_s_statusesArr++;
    }

END_ct_parse_statuses:
    return e_retval;
}

CtError ct_parse_xml(CurlTweet p_s_ctHandle, void *p_v_output, ParseTag e_parse)
{
    CtError e_retval = ERR_NONE;

    p_s_ctHandle->p_s_xml = ezxml_parse_str(p_s_ctHandle->p_u8_payload, p_s_ctHandle->u32_contentLength);
    if (p_s_ctHandle->p_s_xml == NULL) {
        e_retval = ERR_INTERNAL;
        goto END_ct_parse_xml;
    }

    switch(e_parse) {
    case PARSE_STATUS:
        e_retval = ct_parse_status(p_s_ctHandle->p_s_xml, p_v_output, -1);
        printf("retval %d\n", e_retval);
        break;
    case PARSE_STATUSES:
        e_retval = ct_parse_statuses(p_s_ctHandle->p_s_xml, p_v_output);
        break;
    case PARSE_USERS:
        break;
    case PARSE_DIRECT_MESSAGES:
        break;
    case PARSE_RELATIONSHIP:
        break;
    case PARSE_ID_LIST:
        break;
    case PARSE_SAVED_SEARCH:
        break;
    case PARSE_LOCATIONS:
        break;
    }

END_ct_parse_xml:
            return e_retval;
    }

CtError ct_post_and_parse (CurlTweet p_s_ctHandle, void *p_v_output, uint8_t *p_u8_postData,
                                       ParseTag e_parse, uint8_t *p_u8_url)
{
    CtError e_retval = ERR_NONE;
    p_s_ctHandle->p_u8_payload = NULL;
    p_s_ctHandle->u32_contentLength = 0;
    p_s_ctHandle->u32_dataReceived = 0;
    p_s_ctHandle->s32_dataFullyReceived = 0;

    e_retval = ct_prepare_nw(p_s_ctHandle, p_u8_url, p_u8_postData);
    if (e_retval != ERR_NONE) {
        goto END_ct_post_and_parse;
    }

    while (!(p_s_ctHandle->s32_dataFullyReceived)) {
        usleep(10 * 1000);
    }

    e_retval = ct_parse_xml(p_s_ctHandle, p_v_output, e_parse);

    /* Free the downloaded payload as it is copied in p_s_xml */
    free(p_s_ctHandle->p_u8_payload);
    p_s_ctHandle->p_u8_payload = NULL;
    p_s_ctHandle->u32_contentLength = 0;
    p_s_ctHandle->u32_dataReceived = 0;
    p_s_ctHandle->s32_dataFullyReceived = 0;

END_ct_post_and_parse:
    return e_retval;
}
CtError ct_fetch_and_parse(CurlTweet p_s_ctHandle, void *p_v_output,
                                       ParseTag e_parse, uint8_t *p_u8_url)
{
    CtError e_retval = ERR_NONE;
    p_s_ctHandle->p_u8_payload = NULL;
    p_s_ctHandle->u32_contentLength = 0;
    p_s_ctHandle->u32_dataReceived = 0;
    p_s_ctHandle->s32_dataFullyReceived = 0;

    e_retval = ct_prepare_nw(p_s_ctHandle, p_u8_url, NULL);
    if (e_retval != ERR_NONE) {
        goto END_ct_fetch_and_parse;
    }

    while (!(p_s_ctHandle->s32_dataFullyReceived)) {
        usleep(10 * 1000);
    }

    e_retval = ct_parse_xml(p_s_ctHandle, p_v_output, e_parse);

    /* Free the downloaded payload as it is copied in p_s_xml */
    free(p_s_ctHandle->p_u8_payload);
    p_s_ctHandle->p_u8_payload = NULL;
    p_s_ctHandle->u32_contentLength = 0;
    p_s_ctHandle->u32_dataReceived = 0;
    p_s_ctHandle->s32_dataFullyReceived = 0;

END_ct_fetch_and_parse:
    return e_retval;
}
